package com.anji.plus.gaea.security.plus.modules.role.service.impl;

import com.anji.plus.gaea.bean.TreeNode;
import com.anji.plus.gaea.cache.CacheHelper;
import com.anji.plus.gaea.constant.Enabled;
import com.anji.plus.gaea.constant.GaeaConstant;
import com.anji.plus.gaea.constant.GaeaKeyConstant;
import com.anji.plus.gaea.curd.mapper.GaeaBaseMapper;
import com.anji.plus.gaea.security.plus.modules.authority.dao.GaeaAuthorityMapper;
import com.anji.plus.gaea.security.plus.modules.authority.dao.entity.GaeaAuthority;
import com.anji.plus.gaea.security.plus.modules.org.dao.entity.GaeaOrg;
import com.anji.plus.gaea.security.plus.modules.org.service.GaeaOrgService;
import com.anji.plus.gaea.security.plus.modules.role.controller.dto.RoleMenuAuthorityDTO;
import com.anji.plus.gaea.security.plus.modules.role.controller.param.GaeaRoleParam;
import com.anji.plus.gaea.security.plus.modules.role.dao.GaeaRoleMapper;
import com.anji.plus.gaea.security.plus.modules.role.dao.GaeaRoleMenuAuthorityMapper;
import com.anji.plus.gaea.security.plus.modules.role.dao.entity.GaeaRole;
import com.anji.plus.gaea.security.plus.modules.role.dao.entity.GaeaRoleMenuAuthority;
import com.anji.plus.gaea.security.plus.modules.role.service.GaeaRoleService;
import com.anji.plus.gaea.security.plus.modules.user.dao.GaeaUserRoleMapper;
import com.anji.plus.gaea.security.plus.modules.user.dao.entity.GaeaUserRole;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 角色(GaeaRole)ServiceImpl
 *
 * @author lr
 * @since 2021-02-02 13:37:54
 */
@Service
public class GaeaRoleServiceImpl implements GaeaRoleService {

    @Autowired
    private GaeaRoleMapper gaeaRoleMapper;

    @Autowired
    private GaeaOrgService gaeaOrgService;

    @Autowired
    private GaeaUserRoleMapper gaeaUserRoleMapper;

    @Autowired
    private GaeaRoleMenuAuthorityMapper gaeaRoleMenuAuthorityMapper;

    @Autowired
    private GaeaAuthorityMapper gaeaAuthorityMapper;

    @Autowired
    private CacheHelper cacheHelper;

    @Override
    public GaeaBaseMapper<GaeaRole> getMapper() {
        return gaeaRoleMapper;
    }

    /**
     * 查询机构下的角色，当传的是值包含":"时，说明是角色编号
     *
     * @param param        查询参数
     * @param queryWrapper 基本查询条件
     * @return
     */
    @Override
    public Wrapper<GaeaRole> extensionWrapper(GaeaRoleParam param, QueryWrapper<GaeaRole> queryWrapper) {

        List<String> orgCodes = gaeaOrgService.getCurrentUserOrgCodes();
        if (!CollectionUtils.isEmpty(orgCodes)) {
            queryWrapper.lambda().in(GaeaRole::getOrgCode, orgCodes);
        }

        if (StringUtils.isNotBlank(param.getCode())) {
            if (param.getCode().contains(GaeaConstant.REDIS_SPLIT)) {
                queryWrapper.and(wrapper -> wrapper.or().lambda().eq(GaeaRole::getRoleCode, param.getCode().split(GaeaConstant.REDIS_SPLIT)[1]));
            } else {
                queryWrapper.and(wrapper -> wrapper.or().lambda().eq(GaeaRole::getOrgCode, param.getCode()));
            }
        }
        return queryWrapper;
    }


    @Override
    public List<String> getUserRoleCodes(String username) {

        LambdaQueryWrapper<GaeaUserRole> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(GaeaUserRole::getUsername, username);

        List<GaeaUserRole> gaeaUserRoles = gaeaUserRoleMapper.selectList(wrapper);
        return gaeaUserRoles.stream().map(GaeaUserRole::getRoleCode).collect(Collectors.toList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean saveMenuAuthority(RoleMenuAuthorityDTO dto) {
        String roleCode = dto.getRoleCode();
        String orgCode = dto.getOrgCode();
        List<String> authCodes = dto.getCodes();

        //清除菜单的旧关联按钮
        LambdaQueryWrapper<GaeaRoleMenuAuthority> queryWrapper = Wrappers.lambdaQuery();

        queryWrapper.eq(GaeaRoleMenuAuthority::getRoleCode, roleCode);
        queryWrapper.eq(GaeaRoleMenuAuthority::getOrgCode, orgCode);
        queryWrapper.eq(GaeaRoleMenuAuthority::getIsMenu, Enabled.NO.getValue());
        gaeaRoleMenuAuthorityMapper.delete(queryWrapper);


        if (!CollectionUtils.isEmpty(authCodes)) {
            LambdaQueryWrapper<GaeaAuthority> wrapper = Wrappers.lambdaQuery();
            wrapper.in(GaeaAuthority::getAuthCode, authCodes);
            List<GaeaAuthority> gaeaAuthorities = gaeaAuthorityMapper.selectList(wrapper);

            if (!CollectionUtils.isEmpty(gaeaAuthorities)) {
                List<GaeaRoleMenuAuthority> checkList = new ArrayList(authCodes.size());
                //保存新的关联
                gaeaAuthorities.forEach(gaeaAuthority -> {
                    GaeaRoleMenuAuthority gaeaRoleMenuAuthority = new GaeaRoleMenuAuthority();
                    gaeaRoleMenuAuthority.setRoleCode(roleCode);
                    gaeaRoleMenuAuthority.setOrgCode(orgCode);
                    gaeaRoleMenuAuthority.setIsMenu(Enabled.NO.getValue());
                    gaeaRoleMenuAuthority.setMenuCode(gaeaAuthority.getMenuCode());
                    gaeaRoleMenuAuthority.setAuthCode(gaeaAuthority.getAuthCode());
                    gaeaRoleMenuAuthority.setAuthPath(gaeaAuthority.getPath());
                    checkList.add(gaeaRoleMenuAuthority);
                });

                if(!CollectionUtils.isEmpty(checkList)) {
                    gaeaRoleMenuAuthorityMapper.insertBatch(checkList);
                }

            }
        }

        //刷新机构、角色、权限的缓存
        refreshRoleAuthorities(orgCode);
        return true;
    }


    /**
     * 组织树
     *
     * @param orgList
     * @param pCode
     * @return
     */
    private List<TreeNode> buildOrgTree(List<GaeaOrg> orgList, String pCode) {
        List<TreeNode> childList = new ArrayList<>();
        orgList.forEach(orgPO -> {
            if (StringUtils.isNotEmpty(orgPO.getOrgParentCode()) && orgPO.getOrgParentCode().equals(pCode)) {
                TreeNode treeVO = new TreeNode();
                treeVO.setId(orgPO.getOrgCode());
                treeVO.setLabel(orgPO.getOrgName());
                childList.add(treeVO);
            }
        });
        childList.forEach(treeVO -> {
            List<TreeNode> treeList = buildOrgTree(orgList, treeVO.getId());
            if (!treeList.isEmpty()) {
                treeVO.setChildren(treeList);
            }
        });
        return childList;
    }

    /**
     * 获取当前用户拥有的按钮权限
     *
     * @param roleCode
     * @return
     */
    @Override
    public List<String> getSelectAuthorities(String orgCode, String roleCode) {
        LambdaQueryWrapper<GaeaRoleMenuAuthority> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(GaeaRoleMenuAuthority::getOrgCode, orgCode);
        queryWrapper.eq(GaeaRoleMenuAuthority::getIsMenu, Enabled.NO.getValue());
        queryWrapper.eq(GaeaRoleMenuAuthority::getRoleCode, roleCode);
        return gaeaRoleMenuAuthorityMapper.selectList(queryWrapper).stream()
                .map(GaeaRoleMenuAuthority::getAuthCode)
                .collect(Collectors.toList());
    }


    /**
     * 保存角色，菜单信息
     * @param dto
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean saveMenu(RoleMenuAuthorityDTO dto) {

        //第一步：清楚就的菜单
        LambdaQueryWrapper<GaeaRoleMenuAuthority> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(GaeaRoleMenuAuthority::getOrgCode, dto.getOrgCode());
        wrapper.eq(GaeaRoleMenuAuthority::getRoleCode, dto.getRoleCode());
        wrapper.eq(GaeaRoleMenuAuthority::getIsMenu, Enabled.YES.getValue());
        gaeaRoleMenuAuthorityMapper.delete(wrapper);

        if (CollectionUtils.isEmpty(dto.getCodes())) {
            return false;
        }
        List<GaeaRoleMenuAuthority> result = dto.getCodes().stream().map(code -> {
            GaeaRoleMenuAuthority gaeaRoleMenuAuthority = new GaeaRoleMenuAuthority();
            gaeaRoleMenuAuthority.setMenuCode(code);
            gaeaRoleMenuAuthority.setIsMenu(Enabled.YES.getValue());
            gaeaRoleMenuAuthority.setOrgCode(dto.getOrgCode());
            gaeaRoleMenuAuthority.setRoleCode(dto.getRoleCode());
            return gaeaRoleMenuAuthority;
        }).collect(Collectors.toList());

        int i = gaeaRoleMenuAuthorityMapper.insertBatch(result);
        return i > 0 ;
    }

    @Override
    public void refreshRoleAuthorities(String orgCode) {

        LambdaQueryWrapper<GaeaRoleMenuAuthority> wrapper = Wrappers.lambdaQuery();
        if (StringUtils.isNotBlank(orgCode)) {
            wrapper.eq(GaeaRoleMenuAuthority::getOrgCode, orgCode);
        }
        List<GaeaRoleMenuAuthority> gaeaRoleMenuAuthorities = gaeaRoleMenuAuthorityMapper.selectList(wrapper);

        Map<String, Map<String, Set<String>>> orgPathRoleMap = gaeaRoleMenuAuthorities.stream()
                .filter(gaeaRoleMenuAuthority -> StringUtils.isNoneBlank(gaeaRoleMenuAuthority.getAuthPath(), gaeaRoleMenuAuthority.getRoleCode()))
                .collect(Collectors.groupingBy(GaeaRoleMenuAuthority::getOrgCode, Collectors.
                        groupingBy(GaeaRoleMenuAuthority::getAuthPath,
                                Collectors.mapping(GaeaRoleMenuAuthority::getRoleCode, Collectors.toSet()))));


        orgPathRoleMap.entrySet().stream().forEach(entry -> {
            String key = GaeaKeyConstant.HASH_URL_ROLE_KEY + entry.getKey();

            cacheHelper.delete(key);
            Map<String, Set<String>> value = entry.getValue();

            Map<String,String> result = new HashMap<>(value.size());
            value.entrySet().stream().forEach(e -> {
                String entryValue = e.getValue().stream().collect(Collectors.joining(GaeaConstant.SPLIT));
                result.put(e.getKey(), entryValue);
            });

            cacheHelper.hashSet(key, result);
        });
    }
}
